Voxel-based diffusion metrics¶

Many voxel-based metrics can be derived from diffusion weighted imaging, including fractional anisotropy (FA) and the direction of the principal vector (V1). This notebook illustrates methods for viewing these modalities. While the images here are from FSL, you can also load voxel-based images from other tools including AFNI, MRtrix and DSI-studio. This notebook mirrors the NiiVue dti web page.

TODO: ipyniivue does not yet support isForceMouseClickToVoxelCenters

In [1]:
from pathlib import Path

from ipyniivue import download_dataset

BASE_API_URL = "https://niivue.com/demos/images/"
DATA_FOLDER = Path("images")

download_dataset(
    BASE_API_URL,
    dest_folder=DATA_FOLDER,
    files=[
        "FA.nii.gz",
        "V1.nii.gz",
    ],
)
FA.nii.gz already exists.
V1.nii.gz already exists.
Dataset downloaded successfully to images.
In [2]:
import ipywidgets as widgets

from ipyniivue import NiiVue, ShowRender

# Display items

# Load voxels and show user interface.

nv = NiiVue(
    back_color=(1, 1, 1, 1),
    show_3d_crosshair=True,
    multiplanar_show_render=ShowRender.ALWAYS,
    height=1024,
)

nv.load_volumes(
    [
        {"path": DATA_FOLDER / "FA.nii.gz"},
        {"path": DATA_FOLDER / "V1.nii.gz", "opacity": 1},
    ]
)
nv.volumes[0].colorbar_visible = False
nv.opts.is_alpha_clip_dark = True
nv.set_crosshair_width(0.1)
nv.opts.is_force_mouse_click_to_voxel_centers = True

## create widgets

# modulate_options = nv.mesh_shader_names()
modulate_options = {"FA", "V1", "V1xFA", "Lines", "LinesxFA"}


modulate_dropdown = widgets.Dropdown(
    options=modulate_options,
    value="V1",  # Default shader
    description="Shader:",
)


def on_modulate_change(change):
    """Set modulation."""
    new_value = change["new"]
    nv.volumes[0].opacity = 1.0
    nv.volumes[1].opacity = 1.0
    nv.set_modulation_image(nv.volumes[1].id, "")
    nv.opts.is_v1_slice_shader = False
    nv.opts.is_force_mouse_click_to_voxel_centers = True
    if new_value == "FA":
        nv.volumes[1].opacity = 0.0

    elif new_value == "V1":
        nv.volumes[0].opacity = 0.0
        # nv.volumes[1].opacity = 1.0

    elif new_value == "V1xFA":
        nv.set_modulation_image(nv.volumes[1].id, nv.volumes[0].id)

    elif new_value == "Lines":
        nv.opts.is_v1_slice_shader = True

    elif new_value == "LinesxFA":
        nv.opts.is_v1_slice_shader = True
        nv.set_modulation_image(nv.volumes[1].id, nv.volumes[0].id)

    else:
        print(f"Unknown shader option: {new_value}")


modulate_dropdown.observe(on_modulate_change, names="value")

dark_check = widgets.Checkbox(value=True, description="Clip Dark")


def on_dark_check_change(change):
    """Recreate nv with new anti_alias setting and re-apply settings."""
    nv.opts.is_alpha_clip_dark = change["new"]


dark_check.observe(on_dark_check_change, names="value")


@nv.on_image_loaded
def on_image_loaded(volume):
    """Handle event after volume is loaded and ready."""
    modulate_dropdown.value = "LinesxFA"
    nv.move_crosshair_in_vox(0, 0, 0)


## Show widgets and view

widgets.VBox(
    [
        widgets.HBox([modulate_dropdown, dark_check]),
        nv,
    ]
)
Out[2]: